Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
Mac and OpenDoc are trademarks of Apple Computer, Inc.
Introduction
The OpenDoc Human Interface Guidelines specify that when an active frame is deactivated, palettes, modeless dialogs and other utility windows should be hidden. When the frame is next activated, these windows should appear in the same position. Furthermore, when the newly activated frame belongs to a different part bound to the same editor, it is desirable that the windows remain shown throughout, rather than disappearing and reappearing immediately. Naturally the contents of these windows must be updated to reflect the state of the newly active frame.
One way to achieve these goals is to create window objects which are shared by all instances of a given part type. This can be done using per-process global variables in the editor library. This has the additional benefit of reducing memory requirements. There is other information which can be shared by multiple part instances. The menu bar is one possibilty. Another is the various tokenized strings for foci, presentation types and view types.
Implementing shared windows is a little tricky. The technique is described in this document, and demonstrated effectively in the Test Clock part, in which the Display Settings and Alarm Settings dialogs are shared between multiple clock parts.
The key features of the implementation are:
A reference-counted C++ object to hold global data, in this case an ODWindow.
Hiding the window, rather than closing it.
The use of the ODFrame method ChangePart().
Using the presentation type to detect relinquishing focus to a frame of the same type
and the part's ReleaseAll() method releases the globals obect:
if (gClockGlobals)
gClockGlobals->ReleaseGlobals(ev);
The AcquireGlobals() and ReleaseGlobals() methods adjust the ref count of this object, and delete it when the ref count goes to 0, which occurs when the last part bound to the editor is destroyed.
When the shared window is first opened it is created in the usual way. In the Clock part, the source frame of the dialog window is set to the frame that was active when the dialog was invoked. When the user closes the window, the clock part simply hides it. When the window is subsequently opened, the hidden window is simply shown.
The following method is called when the menu item which selects the dialog is chosen:
When the user clicks outside a frame of a clock part, the shared dialog window should be hidden, except when the user clicks in a frame belonging to another clock part, in which case the dialog needs to be switched to belong to that part.
When CommitRelinquishFocus() is called, the Clock part calls its LostSelectionFocus() method, which detects that the frame that requested the focus is one of the same type. This is done here by checking the presentation. If the focus is switching to another part, the shared dialog window is hidden, otherwise we do nothing and wait for the dialog to be switched when the other part acquires focus.
Issue: There may be other ways of testing for the same editor, but this seems reasonable.
When the activating frame acquires the focus, it first calls a method of the global object to retarget the shared dialog windows. It then tells the global object to show dialogs that may have been hidden.
Retargeting the dialog is done by calling the ODFrame method ChangePart() and the ODWindow method SetSourceFrame(). ChangePart removes all facets, detaches the frame from the old part using DisplayFrameRemoved, the attaches it to the new part using DisplayFrameAdded, and adds the facets back.
It can be quite tricky hiding and showing dialogs correctly in response to both focus changes within a document, and process-switching between documents. The approach in Test Clock, which delegates to frame classes and separates the two cases, seemed to work well. The ClockFrame::SuspendFocus and ResumeFocus methods were shown above. The SuspendProcess and ResumeProcess methods below are called in response to suspend/resume events:
fShowWindowOnResume = kODFalse; // It may be hidden by user before next Suspend
fShowWindowOnFocus = kODFalse;
window->Show(ev);
}
}
}
Finally, it is necessary to adjust the dialog contents at various points. The Clock part does this when the dialog frame is first creates, and when the dialog frame is added to a part (which also occurs when switching parts).
The dialog is also adjusted when the user changes from analog to digital display. Since the Clock part is now scriptable and recordable, this is done in the SetData accessor function: